/*
 * Description: 事件日志相关事件处理
 */
#include "log_event_handler.h"
#include <winevt.h>
#include <windows.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <winerror.h>
#include <variant>
#include <cstdio>
#include <cstdlib>
#include <tchar.h>
#include <set>
#include <codecvt>
#include "event_reporter.h"
#include "log.h"
#include "stability_common.h"
#include "performance/utils/registry_utils.h"
#include "text_util/string_proc.h"

#import <msxml6.dll> rename_namespace(_T("MSXML"))

namespace DFX {
bool LogEventHandler::Init()
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        LOG_ERR("[DFX - Stability] - GetStabilityEvents Failed to initialize COM library. Error code =",
            GetLastError());
        return false;
    }

    sysContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextSystem);
    if (sysContext == nullptr) {
        LOG_ERR("[DFX - Stability] - EvtCreateRenderContext failed, err={}", GetLastError());
        return false;
    }

    renderBuffHeader = reinterpret_cast<LPWSTR>(VirtualAlloc(nullptr, N_MAX_RENDER_BUFF, MEM_COMMIT, PAGE_READWRITE));
    if (renderBuffHeader == nullptr) {
        LOG_ERR("[DFX - Stability] - VirtualAlloc buffer failed, err={}", GetLastError());
        EvtClose(sysContext);
        return false;
    }

    renderBuffData = reinterpret_cast<LPWSTR>(VirtualAlloc(nullptr, N_MAX_RENDER_BUFF, MEM_COMMIT, PAGE_READWRITE));
    if (renderBuffData == nullptr) {
        LOG_ERR("[DFX - Stability] - VirtualAlloc buffer failed, err={}", GetLastError());
        VirtualFree(renderBuffHeader, 0, MEM_RELEASE);
        EvtClose(sysContext);
        return false;
    }
    return true;
}

LogEventHandler::~LogEventHandler()
{
    DeInit();
}

void LogEventHandler::DeInit()
{
    if (renderBuffHeader != nullptr) {
        VirtualFree(this->renderBuffHeader, 0, MEM_RELEASE);
    }
    renderBuffHeader = nullptr;

    if (renderBuffData != nullptr) {
        VirtualFree(this->renderBuffData, 0, MEM_RELEASE);
    }
    renderBuffData = nullptr;

    if (sysContext != nullptr) {
        EvtClose(sysContext);
        sysContext = nullptr;
    }
    UnsubscribeEvents();
}

string LogEventHandler::FormatTime(const chrono::system_clock::time_point &tp)
{
    auto time_t = chrono::system_clock::to_time_t(tp);
    tm tm{};
    gmtime_s(&tm, &time_t);

    ostringstream ss;
    ss << put_time(&tm, "%Y-%m-%dT%H:%M:%S");
    auto duration = tp.time_since_epoch();
    auto microseconds = chrono::duration_cast<chrono::microseconds>(duration).count() % MICROSECONDS_OF_SECOND;
    ss << '.' << setw(MICROSECONDS_WIDTH) << setfill('0') << microseconds << 'Z';
    return ss.str();
}

wstring LogEventHandler::GetEventQuery(string startTimeStr)
{
    wostringstream queryStream;
    // 开始构建查询语句
    if (!startTimeStr.empty()) {
        queryStream << L"(";
    }
    set<UINT16> eventIdSet;
    auto it = EVENT_XML_CONFIGS.begin();
    for (size_t i = 0; i < EVENT_XML_CONFIGS.size(); ++i, it++) {
        auto eventId = get<0>(it->first);
        if (eventIdSet.find(eventId) != eventIdSet.end()) {
            continue;
        }
        queryStream << L"Event/System[EventID = " << eventId << L"]";
        if (i != EVENT_XML_CONFIGS.size() - 1) {
            queryStream << L" or ";
        }
        eventIdSet.insert(eventId);
    }
    if (!startTimeStr.empty()) {
        // 添加时间过滤条件
        wstring wstartTimeStr = StringProc::StringToWString(startTimeStr);
        queryStream << L") and Event/System/TimeCreated[@SystemTime>='" << wstartTimeStr << L"']";
    }
    return queryStream.str();
}

// 删除命名空间xmlns 简化xml解析
wstring LogEventHandler::RemoveXmlns(LPWSTR xml)
{
    if (!xml) {
        return wstring();
    }
    wstring xmlStr(xml);
    size_t pos;
    size_t len = strlen(" xmlns'");
    while ((pos = xmlStr.find(L" xmlns")) != wstring::npos) {
        size_t endQuotePos = xmlStr.find_first_of(L"\"'", pos);
        if (endQuotePos == wstring::npos || endQuotePos < pos + len) {
            continue;
        }
        size_t endValuePos = xmlStr.find(xmlStr[endQuotePos], endQuotePos + 1);
        if (endValuePos == wstring::npos) {
            break;
        }
        xmlStr.erase(pos, endValuePos - pos + 1);
    }
    return xmlStr;
}

void LogEventHandler::ProcessXmlEvent(PEVT_VARIANT eventSystemHeader, LPWSTR xmlBufferData)
{
    auto eventId = eventSystemHeader[EvtSystemEventID].UInt16Val;
    auto timeCreated = eventSystemHeader[EvtSystemTimeCreated].FileTimeVal;
    auto eventProviderName = StabilityCommon::LPCWSTRToString(eventSystemHeader[EvtSystemProviderName].StringVal);
    map<string, string> eventData = {};
    auto eventInfo = make_pair(eventId, eventProviderName);
    auto it = EVENT_XML_CONFIGS.find(eventInfo);
    if (it == EVENT_XML_CONFIGS.end()) {
        LOG_WARN("[DFX - Stability] - not found eventId:{}, eventProviderName:{}", eventId, eventProviderName.c_str());
        return;
    }
    wstring xmlData = RemoveXmlns(xmlBufferData);
    try {
        MSXML::IXMLDOMDocument2Ptr xmlDoc;
        HRESULT hr = xmlDoc.CreateInstance(__uuidof(MSXML::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
        if (FAILED(hr)) {
            LOG_ERR("[DFX - Stability] - XmlDoc CreateInstance failed");
            return;
        }
        if (xmlDoc->loadXML(xmlData.c_str()) != VARIANT_TRUE) {
            LOG_ERR("[DFX - Stability] - Unable to load xml Data");
            return;
        }
        auto &properties = get<1>(it->second);
        xmlDoc->setProperty(_T("SelectionLanguage"), _T("XPath"));
        for (auto &property : properties) { // 根据EVENT_XML_CONFIGS的配置, 获取每个属性的值
            auto propertyName = get<0>(property);
            auto xpath = get<1>(property);
            MSXML::IXMLDOMNodePtr singleNode = xmlDoc->selectSingleNode(xpath.c_str());
            if (singleNode != NULL) {
                _variant_t nodeValue = singleNode->text;
                _bstr_t bstrTemp(nodeValue.bstrVal);
                eventData[propertyName] = StabilityCommon::LPCWSTRToString(bstrTemp);
            } else {
                LOG_ERR("[DFX - Stability] - not found xpath::{}", xpath.c_str());
            }
        }
    } catch (_com_error &e) {
        LOG_ERR("[DFX - Stability] - xml parse failed");
        return;
    }
    if (eventData.empty()) {
        LOG_ERR("[DFX - Stability] - eventData is empty");
        return;
    }
    auto eventName = get<0>(it->second);
    if (eventId == EVENTID_APPLICATION_HANG && eventData["ProcessName"] == "explorer.exe") {
        LOG_INFO("[DFX - Stability] - explorer.exe hang, timeCreated: {}", timeCreated);
    }
    EVENT_REPORTER.ReportEventData(eventName, eventData);
}

DWORD LogEventHandler::ProcessEventDatas(EVT_HANDLE eventHandle)
{
    if (eventHandle == nullptr) {
        return ERROR_INVALID_PARAMETER;
    }

    DWORD status = ERROR_SUCCESS;
    EVT_HANDLE hEvents[RENDER_EVENT_ARRAY_SIZE] = { nullptr };
    DWORD eventCount = 0;
    DWORD eventsCnt = 0;
    bool continueProcess = true;

    while (continueProcess) {
        if (EvtNext(eventHandle, RENDER_EVENT_ARRAY_SIZE, hEvents, INFINITE, 0, &eventCount) == FALSE) {
            if ((status = GetLastError()) != ERROR_NO_MORE_ITEMS) {
                LOG_ERR("[DFX - Stability] - EvtNext failed with:{}", status);
            }
            continueProcess = false;
            break;
        }

        for (DWORD i = 0; i < eventCount; i++) {
            DoProcessEvent(hEvents[i]);
        }
    }
    for (DWORD i = 0; i < eventCount; i++) {
        if (hEvents[i] != nullptr) {
            EvtClose(hEvents[i]);
            hEvents[i] = nullptr;
        }
    }
    return status;
}

void LogEventHandler::UnsubscribeEvents()
{
    if (subscriptionAppEvt != nullptr) {
        EvtClose(subscriptionAppEvt);
        subscriptionAppEvt = nullptr;
        LOG_INFO("[DFX - Stability] - Application UnsubscribeEvents success");
    }
    if (subscriptionSysEvt != nullptr) {
        EvtClose(subscriptionSysEvt);
        subscriptionSysEvt = nullptr;
        LOG_INFO("[DFX - Stability] - System UnsubscribeEvents success");
    }
}

void LogEventHandler::SubscribeEvents()
{
    wstring queryStr = GetEventQuery("");
    if (subscriptionAppEvt == nullptr) {
        subscriptionAppEvt = EvtSubscribe(nullptr, nullptr, L"Application", queryStr.c_str(), nullptr, this,
        (EVT_SUBSCRIBE_CALLBACK)EVTCallback, EvtSubscribeToFutureEvents);
        if (subscriptionAppEvt == nullptr) {
            LOG_ERR("[DFX - Stability] - Application EvtSubscribe failed");
        } else {
            LOG_INFO("[DFX - Stability] - Application EvtSubscribe success");
        }
    } else {
        LOG_INFO("[DFX - Stability] - Application EvtSubscribe already exists");
    }
    
    if (subscriptionSysEvt == nullptr) {
        subscriptionSysEvt = EvtSubscribe(nullptr, nullptr, L"System", queryStr.c_str(), nullptr, this,
        (EVT_SUBSCRIBE_CALLBACK)EVTCallback, EvtSubscribeToFutureEvents);
        if (subscriptionSysEvt == nullptr) {
            LOG_ERR("[DFX - Stability] - System EvtSubscribe failed");
        } else {
            LOG_INFO("[DFX - Stability] - System EvtSubscribe success");
        }
    } else {
        LOG_INFO("[DFX - Stability] - System EvtSubscribe already exists");
    }
}

VOID LogEventHandler::DoProcessEvent(EVT_HANDLE event)
{
    // 事件回调会有线程安全问题
    lock_guard<mutex> lck(processEventMutex);
    sysContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextSystem);
    if (sysContext == nullptr) {
        LOG_ERR("[DFX - Stability] - EvtCreateRenderContext failed,err={}", GetLastError());
        return;
    }
    DWORD bufferUsed = 0;
    DWORD propertyCount = 0;
    if (EvtRender(sysContext, event, EvtRenderEventValues, N_MAX_RENDER_BUFF, renderBuffHeader, &bufferUsed,
        &propertyCount) == FALSE) {
        LOG_ERR("[DFX - Stability] - EvtRender failed,err={}", GetLastError());
        EvtClose(event);
        event = nullptr;
        return;
    }

    if (EvtRender(nullptr, event, EvtRenderEventXml, N_MAX_RENDER_BUFF, renderBuffData, &bufferUsed, &propertyCount) ==
        FALSE) {
        LOG_ERR("[DFX - Stability] - EvtRender failed,err={}", GetLastError());
        EvtClose(event);
        event = nullptr;
        return;
    }
    ProcessXmlEvent((PEVT_VARIANT)renderBuffHeader, renderBuffData);
}


DWORD LogEventHandler::EVTCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID userContext, EVT_HANDLE event)
{
    LOG_INFO("[DFX - Stability] - EVTCallback enter");
    if (userContext != nullptr) {
        LogEventHandler *appParam = reinterpret_cast<LogEventHandler *>(userContext);
        appParam->UpdateCheckTimeStr();
        if (action == EvtSubscribeActionDeliver) {
            appParam->DoProcessEvent(event);
        }
    }
    return ERROR_SUCCESS;
}

void LogEventHandler::UpdateCheckTimeStr()
{
    auto now = chrono::system_clock::now();
    lastCheckTimeStr = FormatTime(now);
    REGISTRY_UTILS.SetRegSZ(HROOT, SUBK_EY, ITEM_KEY, lastCheckTimeStr);
}

void LogEventHandler::ReportOldEvents()
{
    DWORD status = ERROR_SUCCESS;
    EVT_HANDLE hResults = nullptr;
    vector<wstring> pwsPaths = { L"Application", L"System" };
    if (lastCheckTimeStr.empty()) {
        LOG_INFO("[DFX - Stability] - boot up, query check time from reg");
        if (!REGISTRY_UTILS.QueryRegSZ(HROOT, SUBK_EY, ITEM_KEY, lastCheckTimeStr)) {
            LOG_INFO("[DFX - Stability] - first time boot, create regKey");
            HKEY hNewKey;
            REGISTRY_UTILS.CreateRegKey(HROOT, SUBK_EY, hNewKey);
        }
    }
    auto now = chrono::system_clock::now();
    if (lastCheckTimeStr.empty()) {
        // 首次安装并开机，取最近5分钟的
        auto startCheckTime = now - chrono::minutes(FIRST_BOOT_CHECK_MINUTES);
        lastCheckTimeStr = FormatTime(startCheckTime);
    }
    // pwsQuery查询格式:
    // L"( (Event/System[EventID = 1000] or Event/System[EventID = 1002]) and
    // Event/System/TimeCreated[@SystemTime>='2024-10-20T02:13:23.981000Z'])";
    wstring pwsQuery = GetEventQuery(lastCheckTimeStr);
    // 更新上次检查的时间
    UpdateCheckTimeStr();
    for (const auto &pwsPath : pwsPaths) {
        hResults = EvtQuery(NULL, pwsPath.c_str(), pwsQuery.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection);
        if (NULL == hResults) {
            LOG_ERR("[DFX - Stability] - EvtQuery failed with:{}", GetLastError());
        } else {
            ProcessEventDatas(hResults);
            EvtClose(hResults);
        }
    }
}
}